// Client side implementation of UDP client-server model
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include <chrono>
#include <thread>
#include <cstdint>
#include <fstream>
#include <string>
#include <deque>
#include <mutex>
#include <vector>
#include <iostream>
#include <algorithm>

std::uint64_t getTimestamp()
{
    return static_cast<std::uint64_t>(
        std::chrono::duration_cast<std::chrono::microseconds>(
            std::chrono::system_clock::now().time_since_epoch())
            .count());
}

#define PORT    2000
#define MAXLINE 1024

// Driver code
int main()
{
    int                sockfd;
    char               buffer[MAXLINE];
    char const* const  hello = "Hello from client";
    struct sockaddr_in servaddr;
    struct sockaddr_in remote_addr;

    // Creating socket file descriptor
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    memset(&servaddr, 0, sizeof(servaddr));

    // Filling server information
    servaddr.sin_family      = AF_INET;
    servaddr.sin_port        = htons(PORT);
    servaddr.sin_addr.s_addr = inet_addr("10.248.173.105");

    ::socklen_t                len;
    std::vector<std::uint64_t> tss;
    tss.reserve(100);

    ::timeval tv;
    tv.tv_sec  = 5;
    tv.tv_usec = 0;

    setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

    for (auto i = 0; i < 3285; ++i)
    {
        std::cout << "SEND_1: " << i << std::endl;
        auto start = getTimestamp();
        auto n     = sendto(sockfd,
                        hello,
                        strlen(hello),
                        0,
                        reinterpret_cast<::sockaddr const*>(&servaddr),
                        sizeof(servaddr));
        tss.push_back(getTimestamp() - start);
        std::cout << "SEND_2: " << i << "|" << n << std::endl;

        if (n <= 0)
        {
            std::cout << "sendto_ERROR: " << n << "|" << errno << std::endl;
            continue;
        }

        len = sizeof(remote_addr); //len is value/result

        std::cout << "RECV_1: " << i << std::endl;
        n = recvfrom(sockfd,
                     buffer,
                     MAXLINE,
                     0,
                     reinterpret_cast<::sockaddr*>(&remote_addr),
                     &len);

        std::cout << "RECV_2: " << i << "|" << n << std::endl;
        if (n <= 0)
        {
            std::cout << "recvfrom_ERROR: " << n << "|" << errno << std::endl;
            continue;
        }

        // std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }

    close(sockfd);

    std::sort(tss.begin(), tss.end());

    auto cnt       = tss.size();
    auto idx_pct99 = cnt * 99 / 100;
    auto idx_pct95 = cnt * 95 / 100;
    auto idx_pct90 = cnt * 90 / 100;
    auto idx_pct75 = cnt * 75 / 100;
    auto idx_pct50 = cnt * 50 / 100;

    auto j = 0;
    for (auto n : tss)
    {
        std::cout << ++j << "|" << n << std::endl;
    }

    std::cout << "Total|PctIndexes: " << cnt << "|" << idx_pct99 << "|"
              << idx_pct95 << "|" << idx_pct90 << "|" << idx_pct75 << "|"
              << idx_pct50 << std::endl;

    std::cout << "tss_pct_99: " << tss[idx_pct99] << std::endl;
    std::cout << "tss_pct_95: " << tss[idx_pct95] << std::endl;
    std::cout << "tss_pct_90: " << tss[idx_pct90] << std::endl;
    std::cout << "tss_pct_75: " << tss[idx_pct75] << std::endl;
    std::cout << "tss_pct_50: " << tss[idx_pct50] << std::endl;

    return 0;
}